/*
 **********************************************************
 *
 * Programme :	liste.c
 *
 * ecrit par :	BAYRAMOV Matin & IBRAHIMLI Nihat
 *
 * resume : 		contient les fonctions utiles
 * 				pour la manupilation sur les listes
 *
 * date :   		Feb 6, 2013
 *
 ***********************************************************
 */

#include <errno.h>

#include "liste.h"

/*******************************************************************
 *
 * Fonction 	: 	createListElement
 *
 * Parametres 	: 	int voisin (numero du voisin)
 * 					double poidsVoisin (poids de l'arrete)
 *
 * Retour 		:	TypVoisins * (une liste contenant un seul element)
 *
 * Description 	: 	fonction crée un TypVoisin, alloue la mémoire
 * 					initialise les pointeurs et les attributs
 *
 *
 * Complexité	:	O(1)
 *
 */
int createListElement(TypVoisins** liste, int voisin, double poidsVoisin) {

	// Il faut typvoisins vide pour en créer une nouvelle
	if ((*liste) != NULL) {
		fprintf(stderr,
				"\ncreateListElement - liste donnee n'est pas null !\n");
		return LISTE_NON_NULL;
	}

	// L'allocation mémoire
	(*liste) = (TypVoisins*) malloc(sizeof(TypVoisins));

	// Vérification de l'allocation mémoire
	if ((*liste) == NULL) {
		fprintf(stderr,
				"\ncreateListElement - echec de malloc a la creation d'un element !\n");
		return PROBLEME_MEMOIRE;
	}

	// Attribution des valeurs
	(*liste)->voisin = voisin;
	(*liste)->poidsVoisin = poidsVoisin;

	// Initialisation des pointeurs
	(*liste)->voisinPrecedent = (*liste);
	(*liste)->voisinSuivant = (*liste);

	return RES_OK;
}

/*******************************************************************
 *
 * Fonction 	: 	addEndElement
 *
 * Parametres 	: 	int voisin (numero du voisin)
 * 					double poidsVoisin (poids de l'arrete)
 * 					TypVoisins** liste (la liste pour y ajouter)
 *
 * Retour 		:	LISTE_NON_NULL si la liste etait vide
 * 					RES_OK si tout est bien passé
 *
 * Description 	: 	fonction ajoute un element à la fin d'une liste
 *
 * Complexité	:	O(n) - n nombre d'elements
 *
 */
int addEndElement(TypVoisins** liste, int voisin, double poidsVoisin) {

	int resultValue = -1;

	// Si la liste n'est pas encore crée
	if ((*liste) == NULL) {
		fprintf(stderr, "\naddEndElement - la liste est vide, crée le "
				"\navant d'ajouter un element\n");
		return LISTE_NON_NULL;
	}

	// Si la liste est mal formé
	if ((*liste)->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr,
				"\naddEndElement - attention la liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	TypVoisins *lastOne = NULL;
	lastOne = (*liste);

	// Chercher le dernier element de la liste
	while (lastOne->voisinSuivant->voisin != FIRST_ELEMT_VALUE) {
		lastOne = lastOne->voisinSuivant;
	}

	// Création d'un nouvel element
	TypVoisins * newElement = NULL;

	// Vérification malloc se fait dans la fonction
	resultValue = createListElement(&newElement, voisin, poidsVoisin);

	// Verification de retour
	if (resultValue != RES_OK) {
		return resultValue;
	}

	// Le dernier element c'est le nouvel element
	lastOne->voisinSuivant = newElement;

	// Le nouvel dernier element est le successeur de l'ancien dernier element
	newElement->voisinPrecedent = lastOne;

	// Le dernier pointe toujours au debut de la liste
	newElement->voisinSuivant = (*liste);

	// Le predecesseur du premier element est le dernier.
	(*liste)->voisinPrecedent = newElement;

	return RES_OK;
}

/*******************************************************************
 *
 * Fonction 	: 	addBeginnningElement
 *
 * Parametres 	: 	int voisin (numero du voisin)
 * 					double poidsVoisin (poids de l'arrete)
 * 					TypVoisins** liste (la liste pour y ajouter)
 *
 * Retour 		:	LISTE_NULL si la liste etait vide
 * 					RES_OK si tout est bien passé
 *
 * Description 	: 	fonction ajoute un element au debut d'une liste
 *
 * Complexité	:	O(1)
 *
 */

int addBeginningElement(TypVoisins** liste, int voisin, double poidsVoisin) {

	int resultValue = -1;

	// Si la liste n'est pas encore crée
	if ((*liste) == NULL) {
		fprintf(stderr, "\naddBeginnningElement - la liste est vide, crée le "
				"\navant d'ajouter un element\n");
		return LISTE_NULL;
	}

	// Si la liste est mal formé
	if ((*liste)->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr,
				"\naddBeginnningElement - attention la liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	// Don't add that's already in
	if (elementExist(*liste, voisin) == RES_OK) {
		return ELEMENT_DEJA_EXISTANT;
	}

	// Création d'un nouvel element
	TypVoisins * newElement = NULL;

	// Vérification malloc se fait dans la fonction
	resultValue = createListElement(&newElement, voisin, poidsVoisin);

	// Verification de retour
	if (resultValue != RES_OK) {
		return resultValue;
	}

	// Insertion de l'element
	newElement->voisinSuivant = (*liste)->voisinSuivant;
	newElement->voisinPrecedent = (*liste);

	// Rebranchement du debut et la suite de la liste
	(*liste)->voisinSuivant->voisinPrecedent = newElement;
	(*liste)->voisinSuivant = newElement;

	return RES_OK;
}


// Clone a type voisins
int cloneTypVoisins(TypVoisins* liste, TypVoisins** clone) {

	// Si la liste n'est pas encore crée
	if (liste == NULL) {
		fprintf(stderr, "\nList is null - cloneTypVoisins\n");
		return LISTE_NULL;
	}

	if ((*clone) != NULL) {
		fprintf(stderr, "\nList clone is not null - cloneTypVoisins\n");
		return LISTE_NON_NULL;
	}

	// Create first default element
	createListElement(clone, -1, 0.0);

	liste = liste->voisinSuivant;

	while (liste->voisin != FIRST_ELEMT_VALUE) {
		addEndElement(clone, liste->voisin, liste->poidsVoisin);
		liste = liste->voisinSuivant;
	}

	return RES_OK;

}

int removeElement(TypVoisins** liste, int voisin) {

	// Si la liste est null
	if ((*liste) == NULL) {
		return LISTE_NULL;
	}

	// Si la liste est mal formé
	if ((*liste)->voisin != FIRST_ELEMT_VALUE) {
		return LISTE_INVALIDE;
	}

	// Si il n'y qu'un seul element par default, retour - il y a rien
	if ((*liste)->voisinSuivant->voisin == FIRST_ELEMT_VALUE) {
		return LISTE_VIDE;
	}

	// Si on veut supprimer element indiquant debut de liste (-1 ici)
	if (voisin == FIRST_ELEMT_VALUE) {
		return REMOVE_ELEMENT_INVALIDE;
	}

	TypVoisins * prevElement = NULL;
	TypVoisins * element = NULL;
	TypVoisins * nextElement = NULL;

	element = (*liste)->voisinSuivant;

	// On parcours la liste pour trouver l'element
	while (element->voisin != FIRST_ELEMT_VALUE && element->voisin != voisin) {
		element = element->voisinSuivant;
	}

	// On a parcouris, mais on la pas trouvé
	if (element->voisin == FIRST_ELEMT_VALUE) {
		return ELEMENT_INEXISTANT;
	}

	// On la forcement trouvé, on debranche cet element
	prevElement = element->voisinPrecedent;
	nextElement = element->voisinSuivant;

	prevElement->voisinSuivant = nextElement;
	nextElement->voisinPrecedent = prevElement;

	element->voisinPrecedent = NULL;
	element->voisinSuivant = NULL;

	free(element);
	element = NULL;

	return RES_OK;
}


int elementExist(TypVoisins* liste, int voisin) {

	// Liste null, rien à faire
	if (liste == NULL) {
		fprintf(stderr,
				"\nelementExist - attention la liste etait nul !\n");
		return LISTE_NULL;
	}

	// Si la liste est mal formé
	if (liste->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr,
				"\nelementExist - attention la liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	liste = liste->voisinSuivant;

	while (liste->voisin != FIRST_ELEMT_VALUE) {

		if (liste->voisin == voisin) {
			return RES_OK;
		}

		liste = liste->voisinSuivant;
	}

	return 0;

}

/*******************************************************************
 *
 * Fonction 	: 	sizeOfElements
 *
 * Parametres 	: 	TypVoisins* liste (la liste à compter les elements)
 *
 * Retour 		:	LISTE_NULL si la liste etait vide
 * 					size_t size (nombre d'elements de la liste)
 *
 * Description 	: 	fonction compte le nombre d'element d'un liste
 *
 * Complexité	:	O(n) - n nombre d'elements
 *
 */

int sizeOfElements(TypVoisins* liste) {

	// Elle est vide, il y en a zero
	if (liste == NULL) {
		fprintf(stderr,
				"\nsizeOfElements - la liste etait null, crée le d'abord\n");
		return LISTE_NULL;
	}

	size_t size = 0;

	do {
		size++;
		liste = liste->voisinSuivant;
	} while (liste->voisin != FIRST_ELEMT_VALUE);

	return size;
}

/*******************************************************************
 *
 * Fonction 	: 	printListe
 *
 * Parametres 	: 	TypVoisins* liste (la liste à afficher)
 *
 * Retour 		:	LISTE_NULL si la liste etait null (pas crée)
 * 					LISTE_INVALIDE si le premiere element n'a pas la bonne valeur associé (-1 ici)
 * 					LISTE_VIDE si il n'y avait aucun element
 * 					RES_OK si tout est bien passé
 *
 * Description 	: 	fonction affiche une liste
 *
 * Complexité	:	O(n) - n nombre d'elements
 *
 */

int printListe(TypVoisins *liste) {

	// Si la liste est vide, rien à faire
	if (liste == NULL) {
		fprintf(stderr, "\nprintListe - attention la liste est vide\n");
		return LISTE_NULL;
	}


	// Si la liste est mal formé
	if (liste->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr, "\nAttention la liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	// Si il n'y qu'un seul element par default on retourn - il y a rien
	if (liste->voisinSuivant->voisin == FIRST_ELEMT_VALUE) {
		return LISTE_VIDE;
	}

	// Sinon on affiche depuis deuxieme elements
	while (liste->voisinSuivant->voisin != FIRST_ELEMT_VALUE) {

		liste = liste->voisinSuivant;
		printf("(%d/%.1f)", liste->voisin + 1, liste->poidsVoisin);
		if (liste->voisinSuivant->voisin != FIRST_ELEMT_VALUE) {
			printf(", ");
		}
	}

	return RES_OK;
}


int printListeInverse(TypVoisins *liste) {

	// Si la liste est vide, rien à faire
	if (liste == NULL) {
		fprintf(stderr, "\nprintListe - attention la liste est vide\n");
		return LISTE_NULL;
	}

	// Si la liste est mal formé
	if (liste->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr, "\nAttention la liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	// Si il n'y qu'un seul element par default on retourn - il y a rien
	if (liste->voisinSuivant->voisin == FIRST_ELEMT_VALUE) {
		return LISTE_VIDE;
	}

	// Sinon on affiche depuis deuxieme elements
	while (liste->voisinPrecedent->voisin != FIRST_ELEMT_VALUE) {
		liste = liste->voisinPrecedent;
		printf("(%d/%f)", liste->voisin + 1, liste->poidsVoisin);
		if (liste->voisinPrecedent->voisin != FIRST_ELEMT_VALUE) {
			printf(", ");
		}
	}


	return RES_OK;
}



int writeListToFile(TypVoisins *liste, FILE * file) {


	// Si la liste est vide, rien à faire
	if (liste == NULL) {
		fprintf(stderr, "\nprintListe - attention la liste est vide\n");
		return LISTE_NULL;
	}

	// Si la liste est mal formé
	if (liste->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr, "\nAttention la liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	// Si il n'y qu'un seul element par default on retourn - il y a rien
	if (liste->voisinSuivant->voisin == FIRST_ELEMT_VALUE) {
		return LISTE_VIDE;
	}

	// Sinon on affiche depuis deuxieme elements
	while (liste->voisinPrecedent->voisin != FIRST_ELEMT_VALUE) {
		liste = liste->voisinPrecedent;
		fprintf(file, "(%d/%.1f)", liste->voisin + 1, liste->poidsVoisin);
		if (liste->voisinPrecedent->voisin != FIRST_ELEMT_VALUE) {
			fprintf(file, ", ");
		}
	}

	return RES_OK;
}

/*******************************************************************
 *
 * Fonction 	: 	freeListe
 *
 * Parametres 	: 	TypVoisins** liste (pointeur sur la liste à afficher)
 *
 * Retour 		:	-1 si la liste etait vide - rien à faire
 * 					-2 si le premiere element n'a pas la valeur -1
 * 					-3 s'il n'y avait qu'un seul element
 * 					-4 si il y avait deux elements
 * 					0 si tout est bien passé
 *
 * Description 	: 	fonction libere la mémoire d'une liste doublement chainnée
 * 					s'il y 3 ou plusieur elements, elle libere le premier à la fin.
 *
 * Complexité	:	O(n) - n nombre d'elements
 *
 */

int freeListe(TypVoisins **liste) {

	if ((*liste) == NULL) {
		fprintf(stderr, "\nfreeListe - nothing to do \n");
		return LISTE_NULL;
	}

	// Si la liste est mal formé
	if ((*liste)->voisin != FIRST_ELEMT_VALUE) {
		fprintf(stderr, "\nfreeListe - liste est mal construit!\n");
		return LISTE_INVALIDE;
	}

	// Il n'y a qu'un seul element
	if ((*liste)->voisinSuivant->voisin == FIRST_ELEMT_VALUE) {
		free(*liste);
		(*liste) = NULL;
		return RES_OK;
	}

	TypVoisins *tmp = NULL;

	tmp = (*liste);
	(*liste) = (*liste)->voisinSuivant;

	// Il n'y a que deux elements
	if ((*liste)->voisinSuivant->voisin == FIRST_ELEMT_VALUE) {
		free(tmp);
		free(*liste);
		tmp = NULL;
		(*liste) = NULL;
		return RES_OK;
	}

	// Il y en a 3 ou plusieurs
	do {
		tmp = NULL;
		tmp = (*liste);
		(*liste) = (*liste)->voisinSuivant;

		tmp->voisinPrecedent = NULL;
		tmp->voisinSuivant = NULL;
		free(tmp);

	} while ((*liste)->voisin != FIRST_ELEMT_VALUE);

	// *liste  point sur le premier element
	// il reste juste liberer celui-la
	free(*liste);

	tmp = NULL;
	(*liste) = NULL;

	return RES_OK;
}

